/*******************************************************************************
 Chatty - Mobile Messaging - J2ME ICQ clone
 Copyright (C) 2003-05  Chatty Project

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ********************************************************************************
 Author(s): Artyomov Denis, Vladimir Kryukov
 *******************************************************************************/

package DrawControls.text;

import DrawControls.icons.Icon;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import chatty.comm.StringConvertor;
import chatty.modules.*;
import chatty.ui.base.CanvasEx;
import chatty.ui.base.GraphicsEx;

/**
 *
 * @author Vladimir Kryukov
 */
public final class Parser {
    private Par par;
    private short maxWidth;
    private int minHeight = 0;
    
    private Vector objects = new Vector();
    private Vector items = new Vector();
    private int lineWidth = 0;
    private Font[] fontSet;

    public Parser(int bigTextIndex, Font[] fontSet, int width) {
        par = new Par(bigTextIndex);
        maxWidth = (short)width;
        this.fontSet = fontSet;
    }
    public void useMinHeight() {
        if (-1 != par.index) {
            minHeight = CanvasEx.minItemHeight;
        }
    }
    public void destroy() {
        objects.removeAllElements();
        objects = null;
        items.removeAllElements();
        items = null;
    }
    public Par getPar() {
        commit();
        return par;
    }
    private int getWidth() {
        return maxWidth;
    }
    
    private int getLastLineWidth() {
        return lineWidth;
    }

    private short addObject(Object obj) {
        objects.addElement(obj);
        return (short)(objects.size() - 1);
    }
    private Icon getIcon(short index) {
        return (Icon)objects.elementAt(index);
    }
    private String getText(short index) {
        return (String)objects.elementAt(index);
    }
    private int getGlyphHeight(Glyphs item) {
        if (-1 != item.imageIndex) {
            return getIcon(item.imageIndex).getHeight();
        }
        return fontSet[item.fontStyle].getHeight();
    }
    private int getGlyphWidth(Glyphs item) {
        if (-1 != item.imageIndex) {
            return getIcon(item.imageIndex).getWidth()
                    + ((-1 != item.textIndex) ? 2 : 0);
        }
        return fontSet[item.fontStyle].substringWidth(
                getText(item.textIndex), item.from, item.to - item.from);
    }

    private short[] getLines(Vector glyphs) {
        int size = 1;
        boolean newLine = true;
        final int glyphCount = glyphs.size();
        
        for (int i = 0; i < glyphCount; ++i) {
            if (newLine) {
                newLine = false;
                size += 1;
            }
            switch (((Glyphs)glyphs.elementAt(i)).type) {
                case 0: size += 7; break;
                case 1: size += 3; break;
                case 2: size += 2; break;
                case 3: size += 1; newLine = true; break;
                case 4: size += 1; newLine = true; break;
            }
        }

        int height = 0;
        int lineHeight = 0;
        int lineHeightOffset = 1;

        newLine = false;
        short[] data = new short[size];
        data[0] = (short)height;
        int ip = 2;
        for (int i = 0; i < glyphCount; ++i) {
            if (newLine) {
                data[lineHeightOffset] = (short)lineHeight;
                height += lineHeight;
                lineHeight = 0;
                newLine = false;
                lineHeightOffset = ip++;
            }
            Glyphs glyph = (Glyphs)glyphs.elementAt(i);
            lineHeight = Math.max(lineHeight, getGlyphHeight(glyph));
            data[ip++] = glyph.type;
            switch (glyph.type) {
                case 0:
                    data[ip++] = glyph.textIndex;
                    data[ip++] = glyph.from;
                    data[ip++] = (short)(glyph.to - glyph.from);
                    data[ip++] = glyph.fontStyle;
                    data[ip++] = glyph.colorType;
                    data[ip++] = (short)getGlyphWidth(glyph);
                    break;

                case 1:
                    data[ip++] = glyph.imageIndex;
                    data[ip++] = glyph.textIndex;
                    break;
                
                case 2:
                    data[ip++] = glyph.imageIndex;
                    break;

                case 3:
                    newLine = true;
                    break;
                
                case 4:
                    newLine = true;
                    break;
            }
        }
        if (lineHeightOffset < data.length) {
            data[lineHeightOffset] = (short)lineHeight;
            height += lineHeight;
        }
        data[0] = (short)Math.max(height, minHeight);
        return data;
    }
    public void commit() {
        Object[] objs = new Object[objects.size()];
        objects.copyInto(objs);
        par.setLines(getLines(items), objs);
    }


    private void internNewLine(boolean br) {
        items.addElement(new Glyphs((byte)(br ? 4 : 3)));
        lineWidth = 0;
    }

    private void internAdd(short textIndex, int from, int to, byte colorType, byte fontStyle) {
		internAdd(new Glyphs(textIndex, from, to, colorType, fontStyle));
	}

    private void internAdd(Glyphs imageItem) {
        items.addElement(imageItem);
        lineWidth += getGlyphWidth(imageItem);
	}

    public void doCRLF() {
        internNewLine(true);
	}

    public void addImage(Icon image) {
		Glyphs img = new Glyphs(addObject(image), (short)-1);
		if ((getLastLineWidth() + getGlyphWidth(img)) > getWidth()) {
			internNewLine(false);
		}
		internAdd(img);
	}

	public void addText(String text, byte colorType, byte fontStyle) {
        addBigText(text, colorType, fontStyle, false);
    }
    
    /**
     * Add big multiline textIndex. 
     * 
     * Text visial width can be larger then screen maxWidth.
     * Method addText automatically divides textIndex to short lines
     * and adds lines to textIndex list
     */
	public void addBigText(String text, byte colorType,
            byte fontStyle, boolean withEmotions) {
		Font font = fontSet[fontStyle];
        short textIndex = addObject(text);
		
		// Width of free space in last line 
        final int fullWidth = getWidth();
		int width = fullWidth - getLastLineWidth();
		int lastWordEnd = -1;
        
        // #sijapp cond.if modules_SMILES is "true" #
        int smileCount = 100;
        // #sijapp cond.end #
        int lineStart = 0;
        int wordStart = 0;
        int wordWidth = 0;
		int textLen = text.length();
        // #sijapp cond.if modules_SMILES is "true" #
        Emotions smiles = Emotions.instance;
        withEmotions &= smiles.isEnabled();
        // #sijapp cond.end #
        for (int i = 0; i < textLen; ++i) {
            char ch = text.charAt(i);
            if ('\n' == ch) {
                internAdd(textIndex, lineStart, i, colorType, fontStyle);
                internNewLine(true);
                lineStart = i + 1;
                width = fullWidth;
                wordStart = lineStart;
                wordWidth = 0;
                continue;
            }
            
            // #sijapp cond.if modules_SMILES is "true" #
            int smileIndex = withEmotions ? smiles.getSmile(text, i) : -1;
            if (-1 != smileIndex) {
                wordStart = i;
                if (lineStart < wordStart) {
                    internAdd(textIndex, lineStart, wordStart, colorType, fontStyle);
                    if (width <= 0) {
                        internNewLine(false);
                        width = fullWidth;
                    }
                }

                Glyphs smileItem = new Glyphs(addObject(smiles.getSmileIcon(smileIndex)),
                        (short)smileIndex);
                width -= getGlyphWidth(smileItem);
                if (width <= 0) {
                    internNewLine(false);
                    width = fullWidth - getGlyphWidth(smileItem);
                }
                internAdd(smileItem);

                i += smiles.getSmileText(smileIndex).length() - 1;
                lineStart = i + 1;
                wordStart = lineStart;
                wordWidth = 0;
                
                smileCount--;
                if (0 == smileCount) {
                    withEmotions = false;
                }
                continue;
            }
            // #sijapp cond.end #
            
            int charWidth = font.charWidth(ch);

            wordWidth += charWidth;
            width -= charWidth;
            if (' ' == ch) {
                wordStart = i + 1;
                wordWidth = 0;
                continue;
            }

            if (width <= 0) {
                if (lineStart < wordStart) {
                    internAdd(textIndex, lineStart, wordStart, colorType, fontStyle);
                    internNewLine(false);
                    lineStart = wordStart;
                    width = fullWidth - wordWidth;
                    continue;

                } else if (wordWidth < fullWidth) {
                    if (0 < getLastLineWidth()) {
                        internNewLine(false);
                    }
                    width = fullWidth - wordWidth;
                    continue;

                } else {
                    internAdd(textIndex, lineStart, i, colorType, fontStyle);
                    internNewLine(false);
                    lineStart = i;
                    width = fullWidth - charWidth;
                    wordStart = i;
                    wordWidth = 0;
                    continue;
                }
            }
		}
        if (lineStart < text.length()) {
            internAdd(textIndex, lineStart, text.length(), colorType, fontStyle);
        }
	}
}
